import sys
import matplotlib.pyplot as plt
import keyboard
import PySpin
import time
import numpy as np

from PyQt5 import QtCore


class PointGreyCam(QtCore.QObject, PySpin.ImageEventHandler):
    imageArrived = QtCore.pyqtSignal(np.ndarray)

    def __init__(self):
        super().__init__()
        self.system = PySpin.System.GetInstance()
        self.cam_list = self.system.GetCameras()

        result = True
        num_cameras = self.cam_list.GetSize()
        if num_cameras == 0:
            # Clear camera list before releasing system
            self.cam_list.Clear()

            # Release system instance
            self.system.ReleaseInstance()

            print('Not enough cameras!')
            input('Done! Press Enter to exit...')
            return False
        self.cam = self.cam_list.GetByIndex(0)
        nodemap = self.cam.GetTLDeviceNodeMap()
        node_device_information = PySpin.CCategoryPtr(nodemap.GetNode('DeviceInformation'))

        if PySpin.IsAvailable(node_device_information) and PySpin.IsReadable(node_device_information):
            features = node_device_information.GetFeatures()
            for feature in features:
                node_feature = PySpin.CValuePtr(feature)
                print('%s: %s' % (node_feature.GetName(),
                                  node_feature.ToString() if PySpin.IsReadable(node_feature) else 'Node not readable'))
                if node_feature.GetName() == 'DeviceModelName':
                    self.deviceName = node_feature.ToString()

        else:
            print('Device control information not available.')
        self.cam.Init()
        self.cam.RegisterEventHandler(self)
        self.cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_Continuous)





    def acquire_images(self, cam, nodemap, nodemap_tldevice):
        """
        This function acquires and saves 10 images from a device.

        :param cam: Camera to acquire images from.
        :param nodemap: Device nodemap.
        :param nodemap_tldevice: Transport layer device nodemap.
        :type cam: CameraPtr
        :type nodemap: INodeMap
        :type nodemap_tldevice: INodeMap
        :return: True if successful, False otherwise.
        :rtype: bool
        """

        print('*** IMAGE ACQUISITION ***\n')
        try:
            result = True
            node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode('AcquisitionMode'))
            if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
                print('Unable to set acquisition mode to continuous (enum retrieval). Aborting...')
                return False

            node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('SingleFrame')
            if not PySpin.IsAvailable(node_acquisition_mode_continuous) or not PySpin.IsReadable(
                    node_acquisition_mode_continuous):
                print('Unable to set acquisition mode to continuous (entry retrieval). Aborting...')
                return False


            acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()

            node_acquisition_mode.SetIntValue(acquisition_mode_continuous)

            print('Acquisition mode set to continuous...')
            cam.BeginAcquisition()

            print('Acquiring images...')
            device_serial_number = ''
            node_device_serial_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceSerialNumber'))
            if PySpin.IsAvailable(node_device_serial_number) and PySpin.IsReadable(node_device_serial_number):
                device_serial_number = node_device_serial_number.GetValue()
                print('Device serial number retrieved as %s...' % device_serial_number)

            # Retrieve, convert, and save images

            try:
                image_result = cam.GetNextImage(1000)

                if image_result.IsIncomplete():
                    print('Image incomplete with image status %d ...' % image_result.GetImageStatus())

                else:

                    width = image_result.GetWidth()
                    height = image_result.GetHeight()
                    print('Grabbed Image , width = %d, height = %d' % (width, height))

                    image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)

                    # Create a unique filename
                    if device_serial_number:
                        filename = 'Acquisition-%s.jpg' % (device_serial_number,)
                    else:  # if serial number is empty
                        filename = 'Acquisition.jpg'

                    image_converted.Save(filename)
                    print('Image saved at %s' % filename)

                    image_result.Release()
                    print('')

            except PySpin.SpinnakerException as ex:
                print('Error: %s' % ex)
                return False

            cam.EndAcquisition()

        except PySpin.SpinnakerException as ex:
            print('Error: %s' % ex)
            return False

        return result




    def startAcquisition(self):
        for i,cam in enumerate(self.cam_list):
            nodemap_tldevice = cam.GetTLDeviceNodeMap()
            nodemap = cam.GetNodeMap()

            try:
                result = True
                node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode('AcquisitionMode'))
                if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
                    print('Unable to set acquisition mode to continuous (enum retrieval). Aborting...')
                    return False

                node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('SingleFrame')
                if not PySpin.IsAvailable(node_acquisition_mode_continuous) or not PySpin.IsReadable(
                        node_acquisition_mode_continuous):
                    print('Unable to set acquisition mode to continuous (entry retrieval). Aborting...')
                    return False

                acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()

                node_acquisition_mode.SetIntValue(acquisition_mode_continuous)



            except PySpin.SpinnakerException as ex:
                print('Error: %s' % ex)
                result = False

            return result

    def getImage(self):
        return self.startGrab(1)

    def startGrab(self, countOfImageGrab):
        imageArray = []
        for i,cam in enumerate(self.cam_list):
            cam.BeginAcquisition()
            for i in range(countOfImageGrab):
                try:
                    image_result = cam.GetNextImage(1000)

                except PySpin.SpinnakerException as ex:
                    print('Error: %s' % ex)
                    return False
            width = image_result.GetWidth()
            height = image_result.GetHeight()
            print('Grabbed Image , width = %d, height = %d' % (width, height))

            image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)
            image_converted = image_converted.GetNDArray()
            imageArray.append(image_converted)
            image_result.Release()
            cam.EndAcquisition()
        return image_converted

    def close(self):
        for i,cam in enumerate(self.cam_list):
            cam.DeInit()
        del cam
        self.cam_list.Clear()
        self.system.ReleaseInstance()

    def OnImageEvent(self, image):
        if image.IsIncomplete():
            print('Image incomplete with image status %i...' % image.GetImageStatus())
        else:
            image_converted = image.Convert(PySpin.PixelFormat_RGB8,
                                                   PySpin.NEAREST_NEIGHBOR)  # PySpin.HQ_LINEAR)

            image_array = image_converted.GetNDArray()
            self.imageCount += 1
        self.imageArrived.emit(image_array)
        print(self.imageCount)







    # def getImage(self):
    #     return self.acquireimage(1)
    #
    # def acquireimage(self,countofimage):
    #     for i,cam in enumerate(self.cam_list):
    #         try:
    #             nodemap = cam.GetNodeMap()
    #             nodemap_tldevice = cam.GetTLDeviceNodeMap()
    #             # 为了访问节点条目，必须将它们转换为指针类型(这里是CEnumerationPtr)
    #             node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode('AcquisitionMode'))
    #             if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
    #                 print('无法将采集模式设置为连续(enum检索)。流产……')  # 无法将获取模式设置为连续，异常终止
    #                 return False
    #
    #             # 从枚举节点检索入口节点
    #             node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('SingleFrame')
    #             if not PySpin.IsAvailable(node_acquisition_mode_continuous) or not PySpin.IsReadable(
    #                     node_acquisition_mode_continuous):
    #                 print('无法将采集模式设置为连续(条目检索)。流产……')
    #                 return False
    #             # 从入口节点检索整数值
    #             acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()
    #             # 设置条目节点的整数值为枚举节点的新值
    #             node_acquisition_mode.SetIntValue(acquisition_mode_continuous)
    #
    #             print('获取模式设置为了连续（continuous）')
    #
    #             # 下面终于到了获取图像啦！
    #             # 注意，相机的获取图像以来获取模式，Single Frame单帧只能单张图片；multi frame多帧，一个图片集多张图片；连续模式获取连续的图像流
    #             # 当没有图片需要时，需要将图片获取结束
    #             self.cam.BeginAcquisition()
    #             print('终于开始获取图片了')
    #
    #             # ****检索设备号，可有可无，方便定义文件名而已
    #             device_serial_number = ''
    #             node_device_serial_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceSerialNumber'))
    #             if PySpin.IsAvailable(node_device_serial_number) and PySpin.IsReadable(node_device_serial_number):
    #                 device_serial_number = node_device_serial_number.GetValue()
    #                 print('设备序列号为%s...' % device_serial_number)
    #
    #             # 检索，转化，存储图片集
    #
    #             try:
    #                 # 注意
    #                 # 捕捉的图片在相机的缓存区，尝试捕获不使相机中止的图片
    #                 # 之后
    #                 # 一旦缓存中的图像被保存并且/或不再需要，图像必须被释放，以保持缓冲区从填充。
    #                 image_result = self.cam.GetNextImage(10)
    #
    #                 # 确保图片是完整的
    #                 # 注意
    #                 # 图片必须是易于检测完整性的。
    #                 if image_result.IsIncomplete():
    #                     print('图片是不完整的，现在图片的状态是%d..' % image_result.GetImageStatus())
    #                 else:
    #                     width = image_result.GetWidth()
    #                     height = image_result.GetHeight()
    #                     print('抓取的图片%d，宽：%d，高：%d' % (i, width, height))
    #
    #
    #                 # 转化图片为单色8
    #                 image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)
    #                 plt.imshow(image_converted)
    #                 plt.show()
    #                 # return image_converted
    #
    #             except PySpin.SpinnakerException as ex:
    #                 print('Error: %s' % ex)
    #                 result = False
    #
    #             # 结束获取,适当地结束收购有助于确保设备得到清理不需要功率循环来保持完整性。
    #             self.cam.EndAcquisition()
    #
    #
    #
    #         except PySpin.SpinnakerException as ex:
    #             print('Error: %s' % ex)
    #             result = False
    #
    #
    #
    #
    # def close(self):
    #     for i in self.cam_list:
    #         self.cam.DeInit()
    #     del self.cam
    #     self.cam_list.Clear()
    #     self.system.ReleaseInstance()
    #
    # def returnCamList(self):
    #     '''
    #
    #     :return: 放回一个相机名字的列表到combo
    #     '''
    #     camList = []
    #     for i,cam in enumerate(self.cam_list):
    #         # camList.append(cam.DeviceUserID())
    #         nodemap_tldevice = cam.GetTLDeviceNodeMap()
    #         node_device_model_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceModelName'))
    #         # node_device_serial_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceSerialNumber'))
    #         device_model_number = node_device_model_number.GetValue()
    #         # device_serial_number = node_device_serial_number.GetValue()
    #         print(device_model_number)
    #         # print(type(node_device_model_number))
    #         camList.append(device_model_number)
    #     return camList

if __name__ == '__main__':

    cam = PointGreyCam()
    cam.openCoummunications()
    cam.startAcquisition()
    for i in range(100):
        img1 = cam.getImage()
        print(i)

    img2 = cam.getImage()
    cam.close()
    plt.subplot(1,2,1)
    plt.imshow(img1)
    plt.subplot(1,2,2)
    plt.imshow(img2)
    plt.show()
    # cam.getImage()


